/****************************************
* James Gomez                           *
* Project 3: Target Practice P3         *
* Comp 465                              *
* Professor Barnes                      *
* 12/7/2012                             *
*****************************************/

#ifndef _3D_OBJECT_
#define _3D_OBJECT_

#include "GomezJamesP3_OpenGL.h"
#include "GomezJamesP3_Vector3D.cpp"

#include <string>
using namespace std;

/***********************************************
Abstract base class for all 3d drawable objects 
************************************************/
class Object3D{

protected:
	enum {Rx, Ry, Rz, Rh, Ux, Uy, Uz, Uh, Ax, Ay, Az, Ah, Tx, Ty, Tz, Th};
	string name;	
	/* stores the current state of this object's transformation matrix.*/
	GLfloat TM[T_MATRIX_SIZE];
	/* a helper matrix for storing transformations to be applied to TM */
	GLfloat OM[T_MATRIX_SIZE];
	
public:	
	Object3D(const string name);
	~Object3D(){}

	inline string getName(){ return name; }	
	
	void getTM(GLfloat matrix[T_MATRIX_SIZE]) const;
	void setTM(const GLfloat newMatrix[T_MATRIX_SIZE]);
	void getLocation(GLfloat &x, GLfloat &y, GLfloat &z) const;
	void setLocation(const GLfloat x, const GLfloat y, const GLfloat z);
	
	Vector3D getLocationV() const { return Vector3D(TM[Tx], TM[Ty], TM[Tz]); };
	Vector3D getRightV() const { return Vector3D(TM[Rx], TM[Ry], TM[Rz]); }
	Vector3D getUpV() const { return Vector3D(TM[Ux], TM[Uy], TM[Uz]); }
	Vector3D getAtV() const { return Vector3D(TM[Ax], TM[Ay], TM[Az]); }
	
	inline GLfloat getX() const{ return TM[Tx]; }
	inline GLfloat getY() const{ return TM[Ty]; }
	inline GLfloat getZ() const{ return TM[Tz]; }
	inline void setX(const GLfloat x){ TM[Tx] = x; }
	inline void setY(const GLfloat y){ TM[Ty] = y; } 
	inline void setZ(const GLfloat z){ TM[Tz] = z; }
	
	virtual void draw() = 0;

protected:
	void getOM(GLfloat matrix[T_MATRIX_SIZE]) const;
	void setOM(const GLfloat newMatrix[T_MATRIX_SIZE]);
	void resetTM();
	void resetOM();
	void drawAxes(GLfloat length);

};


Object3D::Object3D(const string name){ 
	this->name = name; 
	//initialize TM and OM to identity matrix
	resetTM();
	resetOM();
}

void Object3D::getTM(GLfloat matrix[T_MATRIX_SIZE]) const{
//fill matrix with a copy of this object's TM 
	for (int i=0; i<T_MATRIX_SIZE; i++) 
		matrix[i] = TM[i]; 
}

void Object3D::setTM(const GLfloat newMatrix[T_MATRIX_SIZE]){
//set this object's TM to newMatrix
	for(int i=0; i<T_MATRIX_SIZE; i++)
		TM[i] = newMatrix[i]; 
}

void Object3D::getOM(GLfloat matrix[T_MATRIX_SIZE]) const{
	//fill matrix with a copy of this object's OM 
	for (int i=0; i<T_MATRIX_SIZE; i++) 
		matrix[i] = OM[i]; 
}
void Object3D::setOM(const GLfloat newMatrix[T_MATRIX_SIZE]){
	//set this object's TM to newMatrix
	for(int i=0; i<T_MATRIX_SIZE; i++)
		OM[i] = newMatrix[i];
}

void Object3D::getLocation(GLfloat &x, GLfloat &y, GLfloat &z) const{
//set x, y, z to the corresponding location coordinates of this object	
	x = TM[Tx];
	y = TM[Ty];
	z = TM[Tz];
}

void Object3D::setLocation(const GLfloat x, const GLfloat y, const GLfloat z){
//set this object's TM to the location specified by the x,y,z coordinates
	TM[Tx] = x;
	TM[Ty] = y;
	TM[Tz] = z;
}

inline void Object3D::resetTM(){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glGetFloatv(GL_MODELVIEW_MATRIX, TM);
	glPopMatrix();
}

void Object3D::resetOM(){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

/*
draws the orientation vectors of this 3D object with length 
specified by the parameter length
*/
inline void Object3D::drawAxes(GLfloat length){	
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	
	glLineWidth(2);
	glBegin(GL_LINES);		
		glColor3fv(red);
		glVertex3f(0,0,0);
		glVertex3f(length,0,0);

		glColor3fv(green);		
		glVertex3f(0,0,0);
		glVertex3f(0,length,0);

		glColor3fv(blue);	
		glVertex3f(0,0,0);
		glVertex3f(0,0,length);
	glEnd();
	glLineWidth(1);

	glPopMatrix();
}

#endif